#pragma once

#include "../../Libs/headers.default.h"
#include "../../Libs/headers.classification.h"

#include "ConnectionManagers.h"

using namespace loirey;

#define RBM_FLOAT	float

#define	FLAG_AVERAGE_WEIGHT_GRADIENT	0

namespace nw_rbm
{
	class CWeightItem
	{
	public:
		bool fValueFixed;
		RBM_FLOAT Value;
		RBM_FLOAT ConDiv_NextIncrease;
		RBM_FLOAT ConDiv_LastIncrease;
		RBM_FLOAT ConDiv_CumulativeWeight;
		RBM_FLOAT SpaCod_CumulativeError;
		RBM_FLOAT SpaCod_CumulativeDerivative;
		RBM_FLOAT SpaCod_CumulativeWeight;

	public:
		CWeightItem();
		virtual void ClearAll();
		virtual void ClearValue();
		virtual void ClearConDiv();
		virtual void ClearSpaCod();
	};

	class CUnitBasic
	{
	public:
		bool fIsRealValued;
		int BiasIndex;
		RBM_FLOAT Sigma;
	public:
		RBM_FLOAT Energy, Expectation, State;

	public:
		CUnitBasic();
		virtual void myInit(
			bool fIsRealValued,	RBM_FLOAT Sigma, int BiasIndex);
		virtual void ClearValue();
	public:
		virtual RBM_FLOAT InferenceEnergy(
			const CConnectionManager* qConnectionManager,
			int SelfHandle,
			const CWeightItem* PtrTargetBiasWeightItem,
			const CUnitBasic* PtrSourceUnit,
			const CWeightItem* PtrEdgeWeightItem);
	public:
		virtual RBM_FLOAT ComputeExpectation();
	public:
		virtual RBM_FLOAT SampleState_ByEnergy(RBM_FLOAT Energy);
		virtual RBM_FLOAT SampleState_ByExpectation(RBM_FLOAT Expectation);
		virtual RBM_FLOAT SampleState();
	public:
		virtual void HelpOtherUnitsInferenceEnergy(
			const CConnectionManager* qConnectionManager,
			int SelfHandle,
			CUnitBasic* PtrTargetUnit,
			const CWeightItem* PtrEdgeWeightItem);
	};

	class CRestrictedBoltzmannMachine;

	class C_RBM_InferenceHelper
	{
	public:
		C_RBM_InferenceHelper();
		virtual ~C_RBM_InferenceHelper();
		virtual void InferenceEnergy_of_Units_Visible(CRestrictedBoltzmannMachine* pRBM);
		virtual void InferenceEnergy_of_Units_Hidden(CRestrictedBoltzmannMachine* pRBM);
		virtual RBM_FLOAT ComputeEdgeGradient_ConDiv(
			CRestrictedBoltzmannMachine* pRBM,
			const RBM_FLOAT* qVisibleState_Data,
			const RBM_FLOAT* qHiddenExpectation_Data,
			const RBM_FLOAT* qVisibleState_Model,
			const RBM_FLOAT* qHiddenExpectation_Model,
			RBM_FLOAT ExampleWeight, RBM_FLOAT GradientAmendment);
	};

	class CRestrictedBoltzmannMachine
	{
	public:
		typedef	CSimpleTypeArray<RBM_FLOAT>			CList_Value;
		typedef CSimpleTypeArray<CUnitBasic>		CList_UnitBasic;
		typedef CSimpleTypeArray<CWeightItem>		CList_WeightItem;

	public:
		int AmountUnit_Visible;
		CList_UnitBasic ListUnit_Visible;
		int AmountUnit_Hidden;
		CList_UnitBasic ListUnit_Hidden;
	public:
		CConnectionManager* pConnectionManager_Visible;
		CConnectionManager* pConnectionManager_Hidden;
	public:
		int AmountWeightItem_Edge;
		int AmountWeightItem_Visible;
		int AmountWeightItem_Hidden;
		CList_WeightItem ListWeightItem_Edge;
		CList_WeightItem ListWeightItem_Visible;
		CList_WeightItem ListWeightItem_Hidden;
	public:
		int PrintEdgeWeightAmountPerLine;
		int PrintEdgeWeightAmountPerKind;
	public:
		RBM_FLOAT Training_CumulativeVisibleError;
		RBM_FLOAT Training_CumulativeHiddenPenalty;
		RBM_FLOAT Training_CumulativeExampleWeight_VisibleError;
		RBM_FLOAT Training_CumulativeExampleWeight_ConDiv;
		RBM_FLOAT Training_CumulativeExampleWeight_SpaCod;
	public:
		RBM_FLOAT Training_DeltaWeight_ConDiv;
		RBM_FLOAT Training_DeltaWeight_SpaCod;
	public:
		CTimer TimerInference;
		CTimer TimerOther_ConDov;
		//CTimer TimerOther_SpaCod;

	public:
		CRestrictedBoltzmannMachine();
		CRestrictedBoltzmannMachine(const CRestrictedBoltzmannMachine& Another);
		CRestrictedBoltzmannMachine& operator = (const CRestrictedBoltzmannMachine& Another);
		virtual ~CRestrictedBoltzmannMachine();
	public:
		virtual void SetConnectionManagers(
			CConnectionManager* pNewConnectionManager_Visible,
			CConnectionManager* pNewConnectionManager_Hidden);
		virtual void Clear_WeiUniTra_Settings();
		virtual void ClearWeightSettings();
		virtual void ClearUnitSettings();
		virtual void ClearTrainingInfo();
		virtual void ClearTimers();
	public:
		virtual void SetState_of_Units_Visible(const RBM_FLOAT* SrcPtrState);
		virtual void SetState_of_Units_Visible(const CSimpleTypeArray<RBM_FLOAT>& SrcListState);
		virtual void GetState_of_Units_Visible(RBM_FLOAT* DstPtrState) const;
		virtual const RBM_FLOAT* GetState_of_Units_Visible(CSimpleTypeArray<RBM_FLOAT>& DstListState) const;
	public:
		virtual void SetExpectation_of_Units_Visible(const RBM_FLOAT* SrcPtrExpectation);
		virtual void SetExpectation_of_Units_Visible(const CSimpleTypeArray<RBM_FLOAT>& SrcListExpectation);
		virtual void GetExpectation_of_Units_Visible(RBM_FLOAT* DstPtrExpectation) const;
		virtual const RBM_FLOAT* GetExpectation_of_Units_Visible(CSimpleTypeArray<RBM_FLOAT>& DstListExpectation) const;
	public:
		virtual void SetEnergy_of_Units_Visible(const RBM_FLOAT* SrcPtrEnergy);
		virtual void SetEnergy_of_Units_Visible(const CSimpleTypeArray<RBM_FLOAT>& SrcListEnergy);
		virtual void GetEnergy_of_Units_Visible(RBM_FLOAT* DstPtrEnergy) const;
		virtual const RBM_FLOAT* GetEnergy_of_Units_Visible(CSimpleTypeArray<RBM_FLOAT>& DstListEnergy) const;
	public:
		virtual void SetState_of_Units_Hidden(const RBM_FLOAT* SrcPtrState);
		virtual void SetState_of_Units_Hidden(const CSimpleTypeArray<RBM_FLOAT>& SrcListState);
		virtual void GetState_of_Units_Hidden(RBM_FLOAT* DstPtrState) const;
		virtual const RBM_FLOAT* GetState_of_Units_Hidden(CSimpleTypeArray<RBM_FLOAT>& DstListState) const;
	public:
		virtual void SetExpectation_of_Units_Hidden(const RBM_FLOAT* SrcPtrExpectation);
		virtual void SetExpectation_of_Units_Hidden(const CSimpleTypeArray<RBM_FLOAT>& SrcListExpectation);
		virtual void GetExpectation_of_Units_Hidden(RBM_FLOAT* DstPtrExpectation) const;
		virtual const RBM_FLOAT* GetExpectation_of_Units_Hidden(CSimpleTypeArray<RBM_FLOAT>& DstListExpectation) const;
	public:
		virtual void SetEnergy_of_Units_Hidden(const RBM_FLOAT* SrcPtrEnergy);
		virtual void SetEnergy_of_Units_Hidden(const CSimpleTypeArray<RBM_FLOAT>& SrcListEnergy);
		virtual void GetEnergy_of_Units_Hidden(RBM_FLOAT* DstPtrEnergy) const;
		virtual const RBM_FLOAT* GetEnergy_of_Units_Hidden(CSimpleTypeArray<RBM_FLOAT>& DstListEnergy) const;
	public:
		virtual void SetAmountWeightItem_Edge(int AmountWeightItem_Edge);
		virtual void SetAmountWeightItem_Visible(int AmountWeightItem_Visible);
		virtual void SetAmountWeightItem_Hidden(int AmountWeightItem_Hidden);
		virtual int NewWeightItem_Edge();
		virtual int NewWeightItem_Visible();
		virtual int NewWeightItem_Hidden();
		virtual int NewUnit_Visible(
			bool fIsRealValued, RBM_FLOAT Sigma, int WeightItemIndex_Visible);
		virtual int NewUnit_Hidden(
			bool fIsRealValued, RBM_FLOAT Sigma, int WeightItemIndex_Hidden);
		virtual void NewRelation_Edge(
			int UnitIndex_Visible, int UnitIndex_Hidden, int WeightItemIndex_Edge);
	public:
		virtual void InitWeightItems();
		virtual void InitWeightItems(RBM_FLOAT Variance);
	public:
		virtual void ClearWeightItems_Value();
		virtual void ClearWeightItems_ConDiv();
		virtual void ClearWeightItems_SpaCod();
	public:
		virtual void SaveWeightModelToFile(string strFN_WeightModel) const;
		virtual bool LoadWeightModelFromFile(string strFN_WeightModel);
		virtual void SaveWeightModelToStream(ostream& outStream) const;
		virtual bool LoadWeightModelFromStream(istream& inStream);
	public:
		virtual void InferenceEnergy_of_Units_Visible(
			C_RBM_InferenceHelper* p_RBM_InferenceHelper);
		virtual void InferenceEnergy_of_Units_Hidden(
			C_RBM_InferenceHelper* p_RBM_InferenceHelper);
		virtual void ComputeExpectation_of_Units_Visible();
		virtual void ComputeExpectation_of_Units_Hidden();
		virtual void SampleState_of_Units_Visible();
		virtual void SampleState_of_Units_Hidden();
		virtual void FullInferenceFlow_Visible(
			C_RBM_InferenceHelper* p_RBM_InferenceHelper);
		virtual void FullInferenceFlow_Hidden(
			C_RBM_InferenceHelper* p_RBM_InferenceHelper);
	public:
		virtual RBM_FLOAT GetUnitBiasValue_Visible(int UnitIndex_Visible) const;
		virtual RBM_FLOAT GetUnitBiasValue_Hidden(int UnitIndex_Hidden) const;
		virtual void SetBiasOnlyEnergy_of_Units_Visible();
		virtual void SetBiasOnlyEnergy_of_Units_Hidden();
	public:
		virtual void CopyUnitsExpectationToState_Visible();
		virtual void CopyUnitsExpectationToState_Hidden();
	public:
		virtual RBM_FLOAT ComputeGradient_ConDiv_GivenUnitsVisible(
			C_RBM_InferenceHelper* p_RBM_InferenceHelper,
			bool fAlreadyInferencedHidden,
			RBM_FLOAT ExampleWeight, RBM_FLOAT GradientAmendment,
			int AmountInferenceIteration);
		virtual RBM_FLOAT ComputeGradient_SpaCod_GivenUnitsVisible(
			C_RBM_InferenceHelper* p_RBM_InferenceHelper,
			RBM_FLOAT SparseLevel,
			RBM_FLOAT ExampleWeight, RBM_FLOAT GradientAmendment);
		virtual RBM_FLOAT Update_ContrastiveDivergence(
			RBM_FLOAT LearningRate, RBM_FLOAT Decay_Mask, RBM_FLOAT Decay_Bias, RBM_FLOAT Momentum);
		virtual const RBM_FLOAT* MakeHiddenDerivative_for_SparseCoding(
			CSimpleTypeArray<RBM_FLOAT>& vHiddenDerivative);
		virtual RBM_FLOAT Update_SparseCoding(
			RBM_FLOAT LearningRate, RBM_FLOAT SparseCodingLambda);
		virtual RBM_FLOAT GetCumulativeVisibleError() const;
		virtual RBM_FLOAT GetCumulativeHiddenPenalty() const;
	public:
		virtual void ComputeBothGradient(
			C_RBM_InferenceHelper* p_RBM_InferenceHelper,
			RBM_FLOAT SparseLevel,
			RBM_FLOAT ExampleWeight, RBM_FLOAT GradientAmendment,
			int AmountInferenceIteration);
		virtual void UpdateBothGradient(
			RBM_FLOAT LearningRate,
			RBM_FLOAT SpaCod_Lambda,
			RBM_FLOAT ConDiv_Decay_Mask, RBM_FLOAT ConDiv_Decay_Bias, RBM_FLOAT ConDiv_Momentum);
	};

}

